package com.ybw.redis.distributed.lock.service.impl;

import com.ybw.redis.distributed.lock.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * @program: redis-distributed-lock
 * @description:
 * @author: geoffrey
 * @create: 2022-02-17 23:23
 */
@Service
@Slf4j
public class RedisServiceImpl implements RedisService {

    @Resource
    private RedisLockRegistry redisLockRegistry;
    private static final long DEFAULT_EXPIRE_UNUSED = 60000L;

    @Override
    public void lock(String lockKey) {
        Lock lock = obtainLock(lockKey);
        lock.lock();
    }

    @Override
    public boolean tryLock(String lockKey) {
        Lock lock = obtainLock(lockKey);
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(String lockKey, long timeout) {
        Lock lock = obtainLock(lockKey);
        try {
            return lock.tryLock(timeout, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }

    @Override
    public void unlock(String lockKey) {
        try {
            Lock lock = obtainLock(lockKey);
            lock.unlock();
            /**
             * Remove locks last acquired more than 'age' ago that are not currently locked.
             * @param age the time since the lock was last obtained.
             * @throws IllegalStateException if the registry configuration does not support this feature.
             */
            redisLockRegistry.expireUnusedOlderThan(DEFAULT_EXPIRE_UNUSED);
        } catch (Exception e) {
            log.error("分布式锁 [{}] 释放异常", lockKey, e);
        }
    }

    /**
     * @param lockKey
     * @return Lock
     * @throws
     * @methodName: obtainLock
     * @author ybwei
     * @date 2022/2/18 14:22
     */
    private Lock obtainLock(String lockKey) {
        /**
         * Obtains the lock associated with the parameter object.
         * @param lockKey The object with which the lock is associated.
         * @return The associated lock.
         */
        return redisLockRegistry.obtain(lockKey);
    }
}
